#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#include "my_thread.h"
#include "my_thread_list.h"


thread_t_list sleepq=NULL;
thread_t_list runq=NULL;
thread_t_list deadq=NULL;


thread_t current;

/* recuperer l'identifiant du thread courant.
 */
extern thread_t thread_self(void){	
	return current;
}
extern int thread_yield(){
	if(current==NULL){
			thread_init();
		}

	thread_t tmp=current;
	int i=0;
	//on met current a l'arriere de la runq
	if(current->status==RUNNING){
		runq = __thread_t_list_remove(runq,tmp);
		runq = __thread_t_list_insert(runq,tmp);
	}
	thread_t_list_iterator it =__thread_t_list_iterator(runq);
	current= __thread_t_list_iterator_current(it);
	i =swapcontext(&(tmp->context),&(current->context));
	if(i==-1)
		fprintf(stdout,"impossible swap: ");
	return i;
}
/* creer un nouveau thread qui va exécuter la fonction func avec l'argument funcarg.
 * renvoie 0 en cas de succès, -1 en cas d'erreur.
 */


/*
La fonction makecontext() modifie le contexte pointé par ucp (qui a été
       obtenu par un appel à getcontext(2)).  Avant  d'appeler  makecontext(),
       l'appelant   doit  allouer  une  nouvelle  pile  pour  ce  contexte  et
       l'affecter  à  ucp->uc_stack  et  définir  un  contexte  successeur  et
       l'affecter à ucp->uc_link.
*/

//void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
void g(void *(*func)(void *),void ** retval,void * param){

	*retval=func(param);
}

extern int thread_create(thread_t* nthread, void *(*func)(void *), void *funcarg){
	if(current==NULL){
			thread_init();
		}
	thread_t newthread=malloc(sizeof(struct thread));
	newthread->thread_id=current->thread_id+1;
	newthread->status=RUNNING;
	ucontext_t* uc=&(newthread->context);
	ucontext_t previous=current->context;

	uc->uc_stack.ss_size = STACK_SIZE;
	uc->uc_stack.ss_sp = malloc(uc->uc_stack.ss_size);
	uc->uc_link = &previous;

	void** retval=malloc(sizeof(void*));
	getcontext(uc);
	makecontext(uc,(void (*)(void))g,3,func,retval,funcarg);
	*nthread=newthread;
	runq = __thread_t_list_insert(runq,newthread);
	int i=thread_yield();//swapcontext(&(current->context),&uc);
	newthread->retval=*retval;
	return i;
}

int wake(thread_t t){
	sleepq=__thread_t_list_remove (sleepq,t);
	runq=__thread_t_list_insert(runq,t);
	t->status=RUNNING;

	return 0;
}



extern int thread_join(thread_t t,void** retval){
	if(current==NULL){
			thread_init();
		}
	if(t==thread_self()){
		return -1;
	}
	while(!t->status==ENDED){
		thread_yield();
	}
	retval=&(t->retval);
	deadq = __thread_t_list_remove(deadq,t);
	runq = __thread_t_list_remove(runq,t);
	free(t);
	return 0;
}
/* terminer le thread courant en renvoyant la valeur de retour retval.
 * cette fonction ne retourne jamais.
 *
 * L'attribut noreturn aide le compilateur à optimiser le code de
 * l'application (élimination de code mort). Attention à ne pas mettre
 * cet attribut dans votre interface tant que votre thread_exit()
 * n'est pas correctement implémenté (il ne doit jamais retourner).
 */
void thread_init(){
	current=malloc(sizeof(struct thread));

	current->retval=NULL;
	current->status=RUNNING;
	current->execution_time=NULL;
	current->thread_id=0;
	runq = __thread_t_list_new (current);//juste pour initialiser
	}
extern void thread_exit(void *retval){
	if(current==NULL){
		thread_init();
	}
	ucontext_t uc;
	current->retval=retval;
	getcontext(&uc);
	runq = __thread_t_list_remove(runq,current);
	deadq = __thread_t_list_insert(deadq,current);
	current->status=ENDED;
	current->context=*(current->context.uc_link);
	thread_yield();
	exit(0);
}

